//define class
class xctrl {

  constructor(app,mysql) {

    this.app = app;
    this.mysql = mysql;

  }

  async create(req, res) {

    let query = 'INSERT INTO ?? SET ?';
    let params = [];

    params.push(req.app.locals._tableName);
    params.push(req.body);

    var results = await this.mysql.exec(query, params);
    res.status(200).json(results);

  }


  async list(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = ''
    queryParamsObj.params = []

    this.mysql.prepareListQuery(req, res, queryParamsObj, 0);

    let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }


  async nestedList(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = '';
    queryParamsObj.params = [];

    this.mysql.prepareListQuery(req, res, queryParamsObj, 1)

    let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }

  async findOne(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = ''
    queryParamsObj.params = []

    this.mysql.prepareListQuery(req, res, queryParamsObj, 2);

    let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }

  async read(req, res) {

    let query = 'select * from ?? where ';
    let params = [];

    params.push(req.app.locals._tableName);

    let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
      req.params.id.split('___'));


    if (!clause) {
      return res.status(400).send({
        error: "Table is made of composite primary keys - all keys were not in input"
      });
    }

    query += clause;
    query += ' LIMIT 1'

    let results = await this.mysql.exec(query, params);
    res.status(200).json(results);


  }

  async exists(req, res) {

    let query = 'select * from ?? where ';
    let params = [];

    params.push(req.app.locals._tableName);

    let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
      req.params.id.split('___'));

    if (!clause) {
      return res.status(400).send({
        error: "Table is made of composite primary keys - all keys were not in input"
      })
    }

    query += clause;
    query += ' LIMIT 1'

    let results = await this.mysql.exec(query, params);
    res.status(200).json(results);


  }


  async update(req, res) {

    let query = 'REPLACE INTO ?? SET ?';
    let params = [];

    params.push(req.app.locals._tableName);
    params.push(req.body);

    var results = await this.mysql.exec(query, params);
    res.status(200).json(results);

  }

  async patch(req, res) {

    let query = 'UPDATE ?? SET ';
    let keys = Object.keys(req.body);

    // SET clause
    let updateKeys = '';
    for (let i = 0; i < keys.length; ++i) {
      updateKeys += keys[i] + ' = ? '
      if (i !== keys.length - 1)
        updateKeys += ', '
    }

    // where clause
    query += updateKeys + ' where '
    let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
      req.params.id.split('___'));

    if (!clause) {
      return res.status(400).send({
        error: "Table is made of composite primary keys - all keys were not in input"
      })
    }

    query += clause;

    // params
    let params = [];
    params.push(req.app.locals._tableName);
    params = params.concat(Object.values(req.body));

    let results = await this.mysql.exec(query, params);
    res.status(200).json(results);


  }

  async delete(req, res) {

    let query = 'DELETE FROM ?? WHERE ';
    let params = [];

    params.push(req.app.locals._tableName);

    let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
      req.params.id.split('___'));

    if (!clause) {
      return res.status(400).send({
        error: "Table is made of composite primary keys - all keys were not in input"
      });
    }

    query += clause;

    let results = await this.mysql.exec(query, params);
    res.status(200).json(results);


  }

  async bulkInsert(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = ''
    queryParamsObj.params = []
    let results = []

    //console.log(req.app.locals._tableName, req.body);

    this.mysql.prepareBulkInsert(req.app.locals._tableName, req.body, queryParamsObj)

    results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }

  async bulkDelete(req, res) {

    let query = 'delete from ?? where ?? in ';
    let params = [];

    params.push(req.app.locals._tableName);
    params.push(this.mysql.getPrimaryKeyName(req.app.locals._tableName));

    query += '('

    if (req.query && req.query._ids) {
      let ids = req.query._ids.split(',')
      for (var i = 0; i < ids.length; ++i) {
        if (i) {
          query += ','
        }
        query += '?'
        params.push(ids[i])
      }
    }

    query += ')'

    //console.log(query, params);

    var results = await this.mysql.exec(query, params);
    res.status(200).json(results);

  }

  async bulkRead(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = ''
    queryParamsObj.params = []

    this.mysql.prepareListQuery(req, res, queryParamsObj, 3);

    //console.log(queryParamsObj.query, queryParamsObj.params);

    let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }


  async count(req, res) {

    let queryParams = {}

    queryParams.query = 'select count(1) as no_of_rows from ?? ';
    queryParams.params = [];

    queryParams.params.push(req.app.locals._tableName);

    this.mysql.getWhereClause(req.query._where, req.app.locals._tableName, queryParams, ' where ')

    let results = await this.mysql.exec(queryParams.query, queryParams.params);
    res.status(200).json(results);

  }

  async distinct(req, res) {

    let queryParamsObj = {}
    queryParamsObj.query = ''
    queryParamsObj.params = []

    this.mysql.prepareListQuery(req, res, queryParamsObj, 4);

    let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
    res.status(200).json(results);

  }


  async groupBy(req, res) {

    if (req.query && req.query._fields) {

      let queryParamsObj = {}
      queryParamsObj.query = 'select ';
      queryParamsObj.params = [];

      /**************** add columns and group by columns ****************/
      this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query, queryParamsObj)

      queryParamsObj.query += ',count(*) as _count from ?? group by ';
      let tableName = req.app.locals._tableName;
      queryParamsObj.params.push(tableName);

      this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query, queryParamsObj)

      if (!req.query._sort) {
        req.query._sort = {}
        req.query._sort = '-_count'
      }

      /**************** add having clause ****************/
      this.mysql.getHavingClause(req.query._having, req.app.locals._tableName, queryParamsObj, ' having ');

      /**************** add orderby clause ****************/
      this.mysql.getOrderByClause(req.query, tableName, queryParamsObj);

      //console.log(queryParamsObj.query, queryParamsObj.params);
      var results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);

      res.status(200).json(results);

    } else {
      res.status(400).json({message: 'Missing _fields query params eg: /api/tableName/groupby?_fields=column1'})
    }

  }

  async ugroupby(req, res) {

    if (req.query && req.query._fields) {

      let queryParamsObj = {}
      queryParamsObj.query = '';
      queryParamsObj.params = [];
      let uGrpByResults = {}

      /**************** add fields with count(*) *****************/
      let fields = req.query._fields.split(',')

      for (var i = 0; i < fields.length; ++i) {

        uGrpByResults[fields[i]] = []

        if (i) {
          queryParamsObj.query += ' UNION '
        }
        queryParamsObj.query += ' SELECT IFNULL(CONCAT(?,?,??),?) as ugroupby, count(*) as _count from ?? GROUP BY ?? '
        queryParamsObj.params.push(fields[i])
        queryParamsObj.params.push('~')
        queryParamsObj.params.push(fields[i])
        queryParamsObj.params.push(fields[i] + '~')
        queryParamsObj.params.push(req.app.locals._tableName)
        queryParamsObj.params.push(fields[i])
      }

      //console.log(queryParamsObj.query, queryParamsObj.params);
      var results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);

      for (var i = 0; i < results.length; ++i) {

        let grpByColName = results[i]['ugroupby'].split('~')[0]
        let grpByColValue = results[i]['ugroupby'].split('~')[1]

        let obj = {}
        obj[grpByColValue] = results[i]['_count'];

        uGrpByResults[grpByColName].push(obj)

      }

      res.status(200).json(uGrpByResults);

    } else {
      res.status(400).json({message: 'Missing _fields query params eg: /api/tableName/ugroupby?_fields=column1,column2'})
    }
  }


  async aggregate(req, res) {


    if (req.query && req.query._fields) {
      let tableName = req.app.locals._tableName;
      let query = 'select '
      let params = []
      let fields = req.query._fields.split(',');

      for (var i = 0; i < fields.length; ++i) {
        if (i) {
          query = query + ','
        }
        query = query + ' min(??) as ?,max(??) as ?,avg(??) as ?,sum(??) as ?,stddev(??) as ?,variance(??) as ? '
        params.push(fields[i]);
        params.push('min_of_' + fields[i]);
        params.push(fields[i]);
        params.push('max_of_' + fields[i]);
        params.push(fields[i]);
        params.push('avg_of_' + fields[i]);
        params.push(fields[i]);
        params.push('sum_of_' + fields[i]);
        params.push(fields[i]);
        params.push('stddev_of_' + fields[i]);
        params.push(fields[i]);
        params.push('variance_of_' + fields[i]);
      }

      query = query + ' from ??'
      params.push(tableName)

      var results = await this.mysql.exec(query, params);

      res.status(200).json(results);
    } else {
      res.status(400).json({message: 'Missing _fields in query params eg: /api/tableName/aggregate?_fields=numericColumn1'});
    }

  }


  async chart(req, res) {

    let query = ''
    let params = []
    let obj = {}


    if (req.query) {

      let isRange = false
      if (req.query.range) {
        isRange = true
      }

      if (req.query && req.query.min && req.query.max && req.query.step) {

        //console.log(req.params.min, req.params.max, req.params.step);

        obj = this.mysql.getChartQueryAndParamsFromMinMaxStep(req.app.locals._tableName,
          req.query._fields,
          parseInt(req.query.min),
          parseInt(req.query.max),
          parseInt(req.query.step),
          isRange)


      } else if (req.query && req.query.steparray && req.query.steparray.length > 1) {

        obj = this.mysql.getChartQueryAndParamsFromStepArray(req.app.locals._tableName,
          req.query._fields,
          (req.query.steparray.split(',')).map(Number),
          isRange)


      } else if (req.query && req.query.steppair && req.query.steppair.length > 1) {

        obj = this.mysql.getChartQueryAndParamsFromStepPair(req.app.locals._tableName,
          req.query._fields,
          (req.query.steppair.split(',')).map(Number),
          false)


      } else {

        query = 'select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??';
        params = [];

        params.push(req.query._fields);
        params.push(req.query._fields);
        params.push(req.query._fields);
        params.push(req.query._fields);
        params.push(req.app.locals._tableName);

        let _this = this;

        let results = await
          _this.mysql.exec(query, params);

        //console.log(results, results['max'], req.params);

        obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(req.app.locals._tableName,
          req.query._fields,
          results[0]['min'],
          results[0]['max'],
          results[0]['stddev'],
          isRange
        )

      }

      this.mysql.getWhereClause(req.query._where, req.app.locals._tableName, obj, ' where ')

      let results = await
        this.mysql.exec(obj.query, obj.params);

      res.status(200).json(results);

    } else {
      res.status(400).json({message: 'Missing _fields in query params eg: /api/tableName/chart?_fields=numericColumn1'});
    }


  }


  async autoChart(req, res) {

    let query = 'describe ??'
    let params = [req.app.locals._tableName]
    let obj = {}
    let results = []

    let isRange = false
    if (req.query.range) {
      isRange = true
    }


    let describeResults = await this.mysql.exec(query, params)

    //console.log(describeResults);

    for (var i = 0; i < describeResults.length; ++i) {

      //console.log('is this numeric column', describeResults[i]['Type']);

      if (describeResults[i]['Key'] !== 'PRI' && this.mysql.isTypeOfColumnNumber(describeResults[i]['Type'])) {

        query = 'select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??';
        params = [];

        params.push(describeResults[i]['Field']);
        params.push(describeResults[i]['Field']);
        params.push(describeResults[i]['Field']);
        params.push(describeResults[i]['Field']);
        params.push(req.app.locals._tableName);

        let _this = this;

        let minMaxResults = await _this.mysql.exec(query, params);

        //console.log(minMaxResults, minMaxResults['max'], req.params);

        query = ''
        params = []

        obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(req.app.locals._tableName,
          describeResults[i]['Field'],
          minMaxResults[0]['min'],
          minMaxResults[0]['max'],
          minMaxResults[0]['stddev'],
          isRange
        )

        let r = await this.mysql.exec(obj.query, obj.params);

        let resultObj = {}
        resultObj['column'] = describeResults[i]['Field']
        resultObj['chart'] = r

        results.push(resultObj);

      }
    }

    res.status(200).json(results);
  }


}

//expose class
module.exports = xctrl;